//package com.tom.architect02.mnbase.annotation.apt.asm;
//
//import org.junit.Test;
//import org.objectweb.asm.AnnotationVisitor;
//import org.objectweb.asm.ClassReader;
//import org.objectweb.asm.ClassVisitor;
//import org.objectweb.asm.ClassWriter;
//import org.objectweb.asm.MethodVisitor;
//import org.objectweb.asm.Opcodes;
//import org.objectweb.asm.Type;
//import org.objectweb.asm.commons.AdviceAdapter;
//import org.objectweb.asm.commons.Method;
//
//import java.io.FileInputStream;
//import java.io.FileOutputStream;
//
//public class ASMUnitTest {
//    @Test
//    public void test(){
//        try {
//            // 插桩的基本流程。
//            //0.输入文件
//            FileInputStream fis =new FileInputStream("C:\\Users\\ASUS\\AndroidStudioProjects\\JvmAndAsm\\app\\src\\test\\java\\com\\example\\jvmandasm\\InjectTest.class");
//            //1.获取一个分析器，绑定输入流fis
//            ClassReader classReader =new ClassReader(fis);
//            ClassWriter classWriter =new ClassWriter(ClassWriter.COMPUTE_FRAMES);
//            //2.开始插桩，进行class文件的修改。结果填到classWriter里面。
//            classReader.accept(new MyClassVisitor(Opcodes.ASM7, classWriter), ClassReader.EXPAND_FRAMES);
//
//            //3.通过classWriter 将结果输出到byte数组。数组中内容，写入到新文件(回写到原来的文件，就是热修复效果。)
//            byte[] bytes=classWriter.toByteArray();
//            FileOutputStream fos=new FileOutputStream("C:\\Users\\ASUS\\AndroidStudioProjects\\JvmAndAsm\\app\\src\\test\\java\\com\\example\\jvmandasm\\InjectTest2.class");
//
//            fos.write(bytes);
//            fos.close();
//            fis.close();
//        } catch(Exception e){
//            e.printStackTrace();
//        }
//    }
//
//    /**
//     * class访问者，有哪些属性，方法
//     * 用来分析类信息
//     */
//    static class MyClassVisitor extends ClassVisitor {
//
//        public MyClassVisitor(int api) {
//            super(api);
//        }
//
//        public MyClassVisitor(int api, ClassVisitor classVisitor) {
//            super(api, classVisitor);
//        }
//        /**
//         * 当有一个方法执行了，就执行这个API一次。类中有多个方法，这里就会执行多次
//         */
//        @Override
//        public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
//            MethodVisitor methodVisitor=super.visitMethod(access, name, descriptor, signature, exceptions);
//            return new MyMethodVisitor(api,methodVisitor,access,name,descriptor);
//        }
//    }
//    /**
//     * 方法的访问者
//     * 用来分析方法 AdviceAdapter(org.ow2.asm:asm-commons:7.1)
//     */
//    static class MyMethodVisitor extends AdviceAdapter {
//
//        protected MyMethodVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
//            super(api, methodVisitor, access, name, descriptor);
//        }
//        int s;
//        @Override
//        protected void onMethodEnter() { // 方法进入回调
//            super.onMethodEnter();
//            if(!inject){ // 没注解，就不执行；有注解才执行
//                return;
//            }
//            //方法进入的时候调用long startTime= System.currentTimeMillis();
////            INVOKESTATIC java/lang/System.currentTimeMillis ()J
//            invokeStatic(Type.getType("Ljava/lang/System;"),new Method("currentTimeMillis","()J")); // 使用方法签名写，
////            LSTORE 1
//            s=newLocal(Type.LONG_TYPE); // 局部变量表申请空间，存到变量中，s是空间的下标。值已经存储到下标的空间里面。
//            storeLocal(s); // 数据保存。
//        }
//
//        int e;
//        @Override
//        protected void onMethodExit(int opcode) { // 方法退出回调
//            super.onMethodExit(opcode);
//            if(!inject){
//                return;
//            }
////        long endTime= System.currentTimeMillis();
//            //当方法退出的时候调用
////            INVOKESTATIC java/lang/System.currentTimeMillis ()J
////            LSTORE 3
//            invokeStatic(Type.getType("Ljava/lang/System;"),new Method("currentTimeMillis","()J"));
//            e=newLocal(Type.LONG_TYPE);
//            storeLocal(e);
//
//            // ****打印语句****
////        System.out.println("execute time="+(endTime-startTime)+"ms");
////            GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
//            getStatic(Type.getType("Ljava/lang/System;"),"out",Type.getType("Ljava/io/PrintStream;"));
////            NEW java/lang/StringBuilder
//            newInstance(Type.getType("Ljava/lang/StringBuilder;"));
////            DUP
//            dup();
////            INVOKESPECIAL java/lang/StringBuilder.<init> ()V
//            invokeConstructor(Type.getType("Ljava/lang/StringBuilder;"),new Method("<init>","()V")); // 构造方法执行。
////            LDC "execute time="
//            visitLdcInsn("execute time=");
////            INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
//            invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),new Method("append","(Ljava/lang/String;)Ljava/lang/StringBuilder;")); // 执行append方法。
//
////            LLOAD 3
////            LLOAD 1
//            loadLocal(e); // 读取2个时间，减法计算。
//            loadLocal(s);
////            LSUB
//            math(SUB, Type.LONG_TYPE);// 做减法
////            INVOKEVIRTUAL java/lang/StringBuilder.append (J)Ljava/lang/StringBuilder;
////            LDC "ms"
////            INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
////            INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
////            INVOKEVIRTUAL java/io/ PrintStream.println (Ljava/lang/String;)V
//            invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),new Method("append","(J)Ljava/lang/StringBuilder;"));
//            visitLdcInsn(" ms");
//            invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),new Method("append","(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
//            invokeVirtual(Type.getType("Ljava/lang/StringBuilder;"),new Method("toString","()Ljava/lang/String;"));
//            invokeVirtual(Type.getType("Ljava/io/PrintStream;"),new Method("println","(Ljava/lang/String;)V;"));
//        }
//
//        boolean inject=false;
//        /**
//         * 当一个方法执行的时候，有注解的就会调用。
//         * 每读到一个注解，就执行一次
//         * 有注解ASMTest的，才加入相关的代码。
//         */
//        @Override
//        public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
//            System.out.println(getName()+"-->>"+descriptor); // 打印注解的签名
//            if("Lcom/example/jvmandasm/ASMTest;".equals(descriptor)){ // 匹配到注解
//                inject=true;
//            }
//            return super.visitAnnotation(descriptor, visible);
//        }
//    }
//
//}
